package com.daikit.commons.server;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import com.daikit.commons.shared.exception.DkException;
import com.daikit.commons.shared.exception.DkWrapperException;
import com.daikit.commons.shared.exception.context.DkExceptionContext;


/**
 * Error processor
 *
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 */
public abstract class AbstractDkErrorProcessor
{

	/**
	 * @return the code to be used for created {@link DkWrapperException} when processed error is not extending
	 *         {@link DkException}
	 */
	public abstract String getWrapperExceptionCode();

	/**
	 * @return the current {@link HttpServletRequest}
	 */
	public abstract HttpServletRequest getCurrentRequest();

	/**
	 * Get current language isocode
	 *
	 * @return the request language isocode
	 */
	public abstract String getLanguageIsoCode();

	/**
	 * Get logged in user name
	 *
	 * @return the user name
	 */
	public abstract String getUserLoggedName();

	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
	// METHODS
	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

	/**
	 * Process given error thrown during a RPC call. This method may be overridden to provide custom behavior (logging
	 * etc).
	 *
	 * @param e
	 *           the {@link Throwable}
	 * @return the processed exception
	 */
	public DkException processRpcError(final Throwable e)
	{
		return processError(e);
	}

	/**
	 * Process given error
	 *
	 * @param e
	 *           the {@link Throwable}
	 * @return the processed exception
	 */
	public DkException processError(final Throwable e)
	{
		DkException dkException;
		if (e instanceof DkException)
		{
			dkException = (DkException) e;
		}
		else if (e instanceof InvocationTargetException && e.getCause() instanceof DkException)
		{
			dkException = (DkException) e.getCause();
		}
		else
		{
			final Throwable e1 = extractMeaningfulExceptionCause(e);
			String message;
			if (e1 == null)
			{
				message = "No exception";
			}
			else
			{
				message = createDefaultErrorMessagePrefix(e1);
				message += createDefaultErrorMessageSuffix(e1);
			}
			dkException = new DkWrapperException(getWrapperExceptionCode(), message, e1);
		}
		populateException(dkException);
		return dkException;
	}

	protected Throwable extractMeaningfulExceptionCause(final Throwable e)
	{
		return e instanceof InvocationTargetException && e.getCause() != null ? e.getCause() : e;
	}

	protected String createDefaultErrorMessagePrefix(final Throwable e)
	{
		String message = e.getClass().getSimpleName() + ": ";
		message += createDefaultErrorMessagePrefixMethod(e);
		return message;
	}

	protected String createDefaultErrorMessagePrefixMethod(final Throwable e)
	{
		String message = "";
		final StackTraceElement element = e.getStackTrace()[0];
		final int lastIndexOfDot = element.getClassName().lastIndexOf('.');
		if (lastIndexOfDot == -1)
		{
			message += element.getClassName();
		}
		else
		{
			message += element.getClassName().substring(lastIndexOfDot + 1);
		}
		message += "." + element.getMethodName() + "(line " + element.getLineNumber() + ")";
		message += " - ";
		return message;
	}

	protected String createDefaultErrorMessageSuffix(final Throwable e)
	{
		return e.getStackTrace().length == 0 ? "No message" : e.getMessage();
	}

	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
	// PRIVATE UTILS
	// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

	private void populateException(final DkException exception)
	{
		final DkExceptionContext context = exception.getContext();
		if (!context.isPopulated())
		{
			if (context.getCreationDate() == null)
			{
				context.setCreationDate(new Date().toString());
			}
			if (context.getRequest() == null)
			{
				final StringBuffer requestURL = getCurrentRequest().getRequestURL();
				if (getCurrentRequest().getQueryString() != null)
				{
					requestURL.append("?").append(getCurrentRequest().getQueryString());
				}
				final String completeURL = requestURL.toString();
				context.setRequest(completeURL);
			}
			if (context.getLanguageIsoCode() == null)
			{
				context.setLanguageIsoCode(getLanguageIsoCode());
			}
			if (context.getUserLogged() == null)
			{
				context.setUserLoggedName(getUserLoggedName());
			}
			context.buildFromException(exception);
			context.setPopulated(true);
		}
	}


}
